﻿module Preprocessor.IfDef

open System.Text
open Lexer

open History
open Prep
open PrintUtils
open Preprocessor.IfParser
open Utils

let rec count tree = 
    let boolOp bool =
        if bool then 1 else 0
    match tree with
        | Value(x) -> x
        | UnOp(op, expr) -> 
            let v = count expr
            match op with 
                | Op.MINUS_OP  -> -v
                | Op.EXCLAM_OP -> boolOp (v=0)
                | Op.TILDE_OP -> -v-1 
                | _ -> failwith "Unknown Unary operator"
        | BinOp(op, left, right) -> 
            let l = count left
            let r = count right
            match op with 
                | Op.PLUS_OP -> l+r
                | Op.MINUS_OP -> l-r
                | Op.STAR_OP -> l*r
                | Op.SLASH_OP -> l/r
                | Op.PERCENT_OP -> l%r
                | Op.INF_INF_OP -> l<<<r
                | Op.SUP_SUP_OP -> l>>>r
                | Op.INF_EQ_OP -> boolOp (l<=r)
                | Op.SUP_EQ_OP -> boolOp (l>=r) 
                | Op.INF_OP -> boolOp (l<r) 
                | Op.SUP_OP -> boolOp (l>r)
                | Op.EQ_EQ_OP -> boolOp (l=r)
                | Op.EXCLAM_EQ_OP -> boolOp(l<>r)
                | Op.AND_OP -> l&&&r
                | Op.CIRC_OP -> l^^^r
                | Op.PIPE_OP -> l|||r
                | Op.AND_AND_OP -> boolOp ((l<>0) && (r<>0))
                | Op.PIPE_PIPE_OP -> boolOp ((l<>0) || (r<>0))
                | Op.COMMA_OP -> r
                | _ -> failwith "Unknown Binary Operation"
        | TernOp(cond, l, r) -> if (count cond) <> 0 then count l else count r

open Clexer

let rec skipLexemes (context:Context) (prep:Prep) lexemes skipElse =
    let append (strb:StringBuilder) x =  strb.Append((TokenToString x) + " ")
    let depthCount = ref 1;
    let predicate x =
        match x with
           | DIRECTIVE_ENDIF (_) -> 
                depthCount := !depthCount-1
                !depthCount <> 0 
           | DIRECTIVE_ELIF (_) ->
                !depthCount <> 0 && skipElse
           | DIRECTIVE_ELSE  (_) -> 
                !depthCount <> 0 && skipElse
           | DIRECTIVE_IF(_) | DIRECTIVE_IFDEF(_) | DIRECTIVE_IFNDEF(_) -> depthCount := !depthCount+1; true
           | _ -> true
    let tokenSeq = prep.takeWhile predicate lexemes
    let head  = Seq.head tokenSeq
    let str = Seq.fold append (append (new StringBuilder()) head) tokenSeq
    let loc = GetLoc head
    addHistory prep (str.ToString()) "" loc true
    let value =  prep.GetNextLexeme lexemes
    match value with 
    | Some (DIRECTIVE_ELIF(y)) ->
         addHistory prep ("#elif") "" y true
         convert context prep (prep.takeWhile (fun lex -> (PrintUtils.GetLoc lex).lineno = y.lineno) lexemes)
    | Some (DIRECTIVE_ELSE(y)) -> 
         addHistory prep ("#else") "" y true
    | Some (DIRECTIVE_ENDIF (y)) -> 
        addHistory prep ("#endif") "" y true
    | _ -> failwith "Error in token sequence"

and convert (context:Context) (prep:Prep) lexemes = 
    let rec convertLexeme lexemes = 
        seq {
            let value = prep.GetNextLexeme lexemes
            if value.IsSome then
                match value.Value with 
                    | IDENT(a, b) ->
                        match context.DefinesMap.TryGetValue' a with
                        | None -> 
                            addHistory prep "" a b false    
                            yield (IfParser.INT "0")
                        | Some value ->
                            Define.trySubstituteDefine prep a b value lexemes
                    | CST_CHAR(a,b) -> 
                        yield IfParser.INT (string (int a))
                    | CST_INT(a,b) -> 
                        yield IfParser.INT a
                    | CST_FLOAT(_,_) | CST_STRING(_,_) | CST_WSTRING(_,_) | ELLIPSIS(_) | PLUS_EQ(_) | MINUS_EQ(_) | STAR_EQ(_) | SLASH_EQ(_) | PERCENT_EQ(_) 
                    | PIPE_EQ(_) | AND_EQ(_) | CIRC_EQ(_) | INF_INF_EQ(_) | SUP_SUP_EQ(_) | EQ(_) | PLUS_PLUS(_) | MINUS_MINUS(_) | ARROW(_) | LBRACE(_) 
                    | RBRACE(_) | LBRACKET(_) | RBRACKET(_) | SEMICOLON(_) | DOT(_)  -> failwith ("Token isn't valid in #if" + (PrintUtils.TokenToString value.Value))
            
                    | INF_INF(_) -> yield IfParser.INF_INF("")
                    | SUP_SUP(_) -> yield IfParser.SUP_SUP("")
                    | EQ_EQ(_) -> yield IfParser.EQ_EQ("")
                    | EXCLAM_EQ(_) -> yield IfParser.EXCLAM_EQ("")
                    | INF_EQ(_) -> yield IfParser.INF_EQ("")
                    | SUP_EQ(_) -> yield IfParser.SUP_EQ("")
                    | INF(_) -> yield IfParser.INF("")
                    | SUP(_) -> yield IfParser.SUP("")
                    | PLUS(_) -> yield IfParser.PLUS("")
                    | MINUS(_) -> yield IfParser.MINUS("")
                    | STAR(_) -> yield IfParser.STAR("")
                    | SLASH(_) -> yield IfParser.SLASH("")
                    | PERCENT(_) -> yield IfParser.PERCENT("")
                    | EXCLAM(_) -> yield IfParser.EXCLAM("")
                    | AND_AND(_) -> yield IfParser.AND_AND("")
                    | PIPE_PIPE(_) -> yield IfParser.PIPE_PIPE("")
                    | AND(_) -> yield IfParser.AND("")
                    | PIPE(_) -> yield IfParser.PIPE("")
                    | CIRC(_) -> yield IfParser.CIRC("")
                    | QUEST(_) -> yield IfParser.QUEST("")
                    | COLON(_) -> yield IfParser.COLON("")
                    | TILDE(_) -> yield IfParser.TILDE("")
                    | LPAREN(_) -> yield IfParser.LBRACE("")
                    | RPAREN(_) -> yield IfParser.RBRACE("")
                    | COMMA(_) -> yield IfParser.COMMA("")
                    | _ -> yield IfParser.INT "0"
                yield! convertLexeme lexemes
            else yield IfParser.EOF("")
           }
    let tokensSeq = convertLexeme lexemes
    use tokensEnumerator = tokensSeq.GetEnumerator()
    let i = ref 0
    let prevLine = ref 0
    let getNextToken (lexbuf:Lexing.LexBuffer<_>) =          
          tokensEnumerator.MoveNext() |> ignore
          incr i
          tokensEnumerator.Current 
    let tree = IfParser.start getNextToken <| Lexing.LexBuffer<_>.FromString "*trampampam*"
    if (count tree)=0 then skipLexemes context prep lexemes true